home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Xconq 7.1.0 / src / xconq-7.1.0 / mac / maclist.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-07  |  22.6 KB  |  883 lines  |  [TEXT/R*ch]

  1. /* Unit lists for the Mac interface to Xconq.
  2.    Copyright (C) 1992, 1993, 1994, 1995, 1996 Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. #include "conq.h"
  10. #include "macconq.h"
  11.  
  12. static pascal void list_vscroll_fn(ControlHandle control, short code);
  13. static pascal void list_hscroll_fn(ControlHandle control, short code);
  14.  
  15. /* (should be adjusted for expected range of values) */
  16.  
  17. int curactcolw = 10;
  18. int imagecolw = 20;
  19. int namecolw = 100;
  20. int typecolw = 70;
  21. int sidecolw = 70;
  22. int hpcolw = 40;
  23. int acpcolw = 60;
  24. int poscolw = 60;
  25. int supcolw = 40;
  26. int notecolw = 50;
  27.  
  28. /* Sum of non-scrolling field widths. */
  29.  
  30. int fixedfieldwidth;
  31.  
  32. /* Sum of all field widths. */
  33.  
  34. int maxlistwidth;
  35.  
  36. /* The height of the headings lines at the top of the window. */
  37.  
  38. int listtoph = 20;
  39.  
  40. int listtopbaseline = 13;
  41.  
  42. /* The possible heights of each individual list entry. */
  43.  
  44. int smallentryspacing = 18;
  45. int largeentryspacing = 34;
  46.  
  47. int listnum = 1;
  48.  
  49. int lastlisth = -1, lastlistv = -1;
  50.  
  51. ControlActionUPP list_vscroll_proc;
  52. ControlActionUPP list_hscroll_proc;
  53.  
  54. /* Create a new list of units. */
  55.  
  56. void
  57. create_list()
  58. {
  59.     int i;
  60.     List *list = (List *) xmalloc(sizeof(List));
  61.     Rect hscrollrect, vscrollrect;
  62.  
  63.     maxlistwidth = 0;
  64.     maxlistwidth += curactcolw;
  65.     maxlistwidth += imagecolw;
  66.     maxlistwidth += namecolw;
  67.     fixedfieldwidth = maxlistwidth;
  68.     maxlistwidth += typecolw;
  69.     maxlistwidth += sidecolw;
  70.     maxlistwidth += hpcolw;
  71.     maxlistwidth += acpcolw;
  72.     maxlistwidth += poscolw;
  73.     maxlistwidth += nummtypes * supcolw;
  74.     maxlistwidth += notecolw;
  75.  
  76.     DGprintf("Creating a list\n");
  77.     list->sides == -1L;  /* lists every side and indeps too */
  78.     for (i = 0; i < MAXSORTKEYS; ++i) {
  79.         list->sortkeys[i] = bynothing;
  80.     }
  81.     list->sortkeys[0] = byside;
  82.     list->mainsortmi = miViewBySide;
  83.     list->listglimpsed = FALSE;
  84.     list->largeicons = FALSE; /* (should be a preference) */
  85.     list->entryspacing = (list->largeicons ? largeentryspacing : smallentryspacing);
  86.     list->firstvisible = 0;
  87.     init_list_contents(list);
  88.     organize_list_contents(list);
  89.     list->next = listlist;
  90.     listlist = list;
  91.     /* Make a window for the list, give it scrollbars. */
  92.     list->window = GetNewWindow(wList, nil, (WindowPtr) -1L);
  93.     stagger_window(list->window, &lastlisth, &lastlistv);
  94.     ShowWindow(list->window);
  95.     SetPort(list->window);
  96.     /* Create the scrollbars. */
  97.     hscrollrect = list->window->portRect;
  98.     hscrollrect.top = hscrollrect.bottom - sbarwid;
  99.     hscrollrect.bottom += 1;
  100.     hscrollrect.left = fixedfieldwidth - 1;
  101.     hscrollrect.right -= sbarwid - 1;
  102.     list->hscrollbar = NewControl(list->window, &hscrollrect, "\p", TRUE,
  103.                                    0, 0, 100, scrollBarProc, 0L);
  104.     vscrollrect = list->window->portRect;
  105.     vscrollrect.top = listtoph - 1;
  106.     vscrollrect.bottom -= sbarwid - 1;
  107.     vscrollrect.left = vscrollrect.right - sbarwid;
  108.     vscrollrect.right += 1;
  109.     list->vscrollbar = NewControl(list->window, &vscrollrect, "\p", TRUE,
  110.                                    0, 0, 100, scrollBarProc, 0L);
  111.     /* Now set the scrollbars to their *real* limits. */
  112.     set_list_scrollbars(list);
  113.     sprintf(spbuf, "List %d", listnum++);
  114.     add_window_menu_item(spbuf, list->window);
  115. }
  116.  
  117. /* Make the list be empty. */
  118.  
  119. void
  120. init_list_contents(List *list)
  121. {
  122.     /* Set up a unit vector with a first cut at needed space;
  123.        will adjust upwards automatically if necessary. */
  124.     list->contents = make_unit_vector(numunits + 50);
  125.     list->numunits = 0;
  126. }
  127.  
  128. /* This takes the list and fills in the items it is to display. */
  129.  
  130. void
  131. organize_list_contents(List *list)
  132. {
  133.     Side *side2;
  134.     Unit *unit;
  135.  
  136.     /* Build up the array of units for this list. */
  137.     list->numunits = 0;
  138.     clear_unit_vector(list->contents);
  139.     /* We always see our own units. */
  140.     for_all_side_units(dside, unit) {
  141.         add_unit_to_list(list, unit);
  142.     }
  143.     for_all_sides(side2) {
  144.         if (dside != side2) {
  145.             for_all_side_units(side2, unit) {
  146.                 if (side_sees_image(dside, unit)) {
  147.                     add_unit_to_list(list, unit);
  148.                 }
  149.             }
  150.         }
  151.     }
  152.     for_all_side_units(indepside, unit) {
  153.         if (side_sees_image(dside, unit)) {
  154.             add_unit_to_list(list, unit);
  155.         }
  156.     }
  157.     /* Now sort the list according to its keys. */
  158.     sort_list_contents(list);
  159. }
  160.  
  161. void
  162. sort_list_contents(List *list)
  163. {
  164.     int i;
  165.  
  166.     for (i = 0; i < MAXSORTKEYS; ++i) {
  167.         tmpsortkeys[i] = list->sortkeys[i];
  168.     }
  169.     sort_unit_vector(list->contents);
  170. }
  171.  
  172. void
  173. add_unit_to_list(List *list, Unit *unit)
  174. {
  175.     if (alive(unit)) {
  176.         list->contents = add_unit_to_vector(list->contents, unit, FALSE);
  177.         /* (should apply other inclusion criteria too?) */
  178.         ++list->numunits;
  179.     }
  180. }
  181.  
  182. /* Calculate reasonable/valid values and maxima for the scrollbars,
  183.    starting from the window size. */
  184.  
  185. void
  186. set_list_scrollbars(List *list)
  187. {
  188.     int numvisunits, visfieldwidth, val, maxval;
  189.     Rect winrect = list->window->portRect;
  190.  
  191.     /* Set the vertical scrollbar correctly. */    
  192.     numvisunits = (winrect.bottom - winrect.top - sbarwid - listtoph) / list->entryspacing;
  193.     maxval = max(0, list->numunits - numvisunits);
  194.     val = GetCtlValue(list->vscrollbar);
  195.     SetCtlMax(list->vscrollbar, maxval);
  196.     if (val > maxval) {
  197.         SetCtlValue(list->vscrollbar, maxval);
  198.         list->firstvisible = maxval;
  199.     }
  200.     /* Set up the horizontal scrollbar. */
  201.     visfieldwidth = winrect.right - winrect.left - sbarwid - fixedfieldwidth;
  202.     val = GetCtlValue(list->hscrollbar);
  203.     maxval = (maxlistwidth - fixedfieldwidth) - visfieldwidth;
  204.     maxval = max(0, maxval);
  205.     SetCtlMax(list->hscrollbar, maxval);
  206.     if (val > maxval) {
  207.         SetCtlValue(list->hscrollbar, maxval);
  208.         list->firstvisfield = maxval;
  209.     }
  210. }
  211.  
  212. List *
  213. list_from_window(WindowPtr window)
  214. {
  215.     List *list;
  216.     
  217.     if (dside == NULL) return NULL;
  218.     for_all_lists(list) {
  219.         if (list->window == window) return list;
  220.     }
  221.     return NULL;
  222. }
  223.  
  224. void
  225. draw_list(List *list)
  226. {
  227.     WindowPtr listwin = list->window;
  228.     Rect tmprect, cliprect;
  229.     RgnHandle tmprgn;
  230.  
  231.     tmprgn = NewRgn();
  232.     GetClip(tmprgn);
  233.     tmprect = listwin->portRect;
  234.     tmprect.right -= sbarwid;
  235.     tmprect.bottom -= sbarwid;
  236.     BackPat(QDPat(white));
  237.     EraseRect(&tmprect);
  238.     /* Set up clipping for the contents of the list. */
  239.     cliprect = listwin->portRect;
  240.     cliprect.right -= sbarwid;
  241.     tmprect.bottom -= sbarwid;
  242.     ClipRect(&cliprect);
  243.     TextSize(9);
  244.     draw_list_contents(list);
  245.     SetClip(tmprgn);
  246.     DisposeRgn(tmprgn);
  247. }
  248.  
  249. void
  250. draw_list_contents(List *list)
  251. {
  252.     int line, numvisunits, viswidth;
  253.     Rect winrect = list->window->portRect;
  254.  
  255.     /* Image is basically square, but add a bit of extra space on each side. */
  256.     imagecolw = list->entryspacing + 2;
  257.     /* Draw the selection and sorting as a sort of header. */
  258.     draw_list_headings(list);
  259.     /* Compute how many list elements are actually visible. */
  260.     numvisunits = (winrect.bottom - winrect.top - listtoph - sbarwid) / list->entryspacing;
  261.     numvisunits = min(numvisunits, list->numunits);
  262.     list->lastvisible = list->firstvisible + numvisunits - 1;
  263.     for (line = list->firstvisible; line <= list->lastvisible; ++line) {
  264.         draw_unit_list_entry(list, line, FALSE);
  265.     }
  266.     HiliteControl(list->vscrollbar, ((numvisunits < list->numunits) ? 0 : 255));
  267.     viswidth = winrect.right - winrect.left - sbarwid;
  268.     HiliteControl(list->hscrollbar, ((viswidth < maxlistwidth) ? 0 : 255));
  269. }
  270.  
  271. void
  272. draw_list_headings(List *list)
  273. {
  274.     int x = 0, m;
  275.     Str255 tmpstr;
  276.     Rect cliprect, tmprect, winrect = list->window->portRect;
  277.     RgnHandle tmprgn;
  278.  
  279.     /* Save the current clip region. */
  280.     tmprgn = NewRgn();
  281.     GetClip(tmprgn);
  282.     cliprect = winrect;
  283.     cliprect.right -= sbarwid;
  284.     ClipRect(&cliprect);
  285.     /* Clear the heading area. */
  286.     SetRect(&tmprect, 0, 0, winrect.right - sbarwid, listtoph);
  287.     EraseRect(&tmprect);
  288.     /* Draw dividing lines that cross both fixed and scrolling fields. */
  289.     MoveTo(0, listtoph - 3);
  290.     Line(winrect.right, 0);
  291.     MoveTo(0, listtoph - 1);
  292.     Line(winrect.right, 0);
  293.     /* (should underline sort keys with varying line heaviness) */
  294.     /* We have to do MoveTo everywhere because DrawString moves the pen. */
  295.     /* First draw headings for fields not affected by horizontal scrolling. */
  296.     x += curactcolw;
  297.     MoveTo(x, listtopbaseline);
  298.     DrawString("\ps/L");
  299.     x += imagecolw;
  300.     MoveTo(x, listtopbaseline);
  301.     DrawString("\pName/Number");
  302.     /* Shift left by horiz scroll. */
  303.     x -= list->firstvisfield;
  304.     /* Now clip against the fixed fields' area. */
  305.     cliprect = winrect;
  306.     cliprect.right -= sbarwid;
  307.     cliprect.left = fixedfieldwidth;
  308.     ClipRect(&cliprect);
  309.     x += namecolw;
  310.     MoveTo(x, listtopbaseline);
  311.     DrawString("\pType");
  312.     x += typecolw;
  313.     MoveTo(x, listtopbaseline);
  314.     DrawString("\pSide");
  315.     x += sidecolw;
  316.     MoveTo(x, listtopbaseline);
  317.     DrawString("\pHp");
  318.     x += hpcolw;
  319.     MoveTo(x, listtopbaseline);
  320.     DrawString("\pAcp");
  321.     x += acpcolw;
  322.     MoveTo(x, listtopbaseline);
  323.     DrawString("\pLoc");
  324.     x += poscolw;
  325.     for_all_material_types(m) {
  326.         MoveTo(x, listtopbaseline);
  327.         c2p(m_type_name(m), tmpstr);
  328.         DrawString(tmpstr);
  329.         x += supcolw;
  330.     }
  331.     MoveTo(x, listtopbaseline);
  332.     DrawString("\pNotes");
  333.     x += notecolw;
  334.     /* Draw a gray line indicating the end of the data columns. */
  335.     PenPat(QDPat(gray));
  336.     MoveTo(x, 0);
  337.     Line(0, listtoph - 4);
  338.     PenNormal();
  339.     /* Restore the clipping. */
  340.     SetClip(tmprgn);
  341.     DisposeRgn(tmprgn);
  342. }
  343.  
  344. /* This draws a one-line entry for the given unit. */
  345.  
  346. /* This routine does *not* save/restore the clip region, but does
  347.    modify it, so it should not be used except in a safe context. */
  348.  
  349. void
  350. draw_unit_list_entry(List *list, int n, int clearfirst)
  351. {
  352.     int u, m, x, y = (n - list->firstvisible) * list->entryspacing + listtoph;
  353.     int texty = y + 15;
  354.     char tmpnbuf[BUFSIZE];
  355.     Side *side2;
  356.     Rect entryrect, tmprect;
  357.     Unit *unit = list->contents->units[n].unit;
  358.     Rect cliprect;
  359.     Rect bbox = (*(list->window->visRgn))->rgnBBox;
  360.  
  361.     SetRect(&entryrect, 0, y, list->window->portRect.right, y + list->entryspacing);
  362. #if 0
  363.     /* (should use rect intersection?) */
  364.     if (!between(bbox.top, y, bbox.bottom)
  365.         && !between(bbox.top, y+list->entryspacing, bbox.bottom)) return;
  366. #endif
  367.     cliprect = list->window->portRect;
  368.     cliprect.right -= sbarwid;
  369.     ClipRect(&cliprect);
  370.  
  371.     if (clearfirst) {
  372.         EraseRect(&entryrect);
  373.     }
  374.     if (unit == NULL || !alive(unit)) {
  375.         /* We need to recalculate the list contents. */
  376.         list->shouldreorg = TRUE;
  377.         return;
  378.     }
  379.     u = unit->type;
  380.     /* Draw whether the unit is awake or asleep. */
  381.     if (unit->plan && completed(unit)) {
  382.         SetRect(&tmprect, 0, y + list->entryspacing/2 - curactcolw/2,
  383.                 curactcolw, y + list->entryspacing/2 + curactcolw/2);
  384.         InsetRect(&tmprect, 2, 2);
  385.         /* (should draw the following analogously to map display) */
  386.         if (unit->plan->asleep) {
  387.             /* Leave rectangle blank. */
  388.         } else if (unit->plan->reserve) {
  389.             FillRect(&tmprect, QDPat(gray));
  390.         } else {
  391.             FillRect(&tmprect, QDPat(black));
  392.         }
  393.         FrameRect(&tmprect);
  394.     }
  395.     /* Draw an icon with side emblem for this unit. */
  396.     draw_unit_image(list->window, curactcolw + 2, y + 1,
  397.                     (list->largeicons ? 32 : 16), (list->largeicons ? 32 : 16),
  398.                     u, side_number(unit->side), !completed(unit));
  399.     /* Write the name or ordinal number. */
  400.     name_or_number(unit, spbuf);
  401.     spbuf[15] = '\0';  /* (should be clipping by pixels) */
  402.     x = curactcolw + imagecolw;
  403.     MoveTo(x, texty);
  404.     DrawText(spbuf, 0, strlen(spbuf));
  405.     /* Adjust according to the horizontal scroll. */
  406.     x -= list->firstvisfield;
  407.     cliprect = list->window->portRect;
  408.     cliprect.right -= sbarwid;
  409.     cliprect.left = fixedfieldwidth;
  410.     ClipRect(&cliprect);
  411.     /* Write the name of the unit's type. */
  412.     x += namecolw;
  413.     MoveTo(x, texty);
  414.     sprintf(spbuf, "%s", u_type_name(u));
  415.     spbuf[10] = '\0';
  416.     DrawText(spbuf, 0, strlen(spbuf));
  417.     /* Write the side of the unit. */
  418.     x += typecolw;
  419.     MoveTo(x, texty);
  420.     side2 = unit->side;
  421.     strcpy(tmpnbuf, shortest_side_title(side2, spbuf));
  422.     tmpnbuf[15] = '\0'; /* truncate long side names */
  423.     DrawText(tmpnbuf, 0, strlen(tmpnbuf));
  424.     /* Draw the current hit points of the unit. */
  425.     x += sidecolw;
  426.     if (side_sees_unit(dside, unit)) {
  427.         MoveTo(x, texty);
  428.         hp_desc(spbuf, unit, FALSE);
  429.         DrawText(spbuf, 0, strlen(spbuf));
  430.     }
  431.     x += hpcolw;
  432.     if (side_sees_unit(dside, unit) && inside_area(unit->x, unit->y)) {
  433.         MoveTo(x, texty);
  434.         acp_desc(spbuf, unit, FALSE);
  435.         if (strlen(spbuf) == 0)
  436.           strcpy(spbuf, "-");
  437.         DrawText(spbuf, 0, strlen(spbuf));
  438.     }
  439.     /* Draw the location. */
  440.     x += acpcolw;
  441.     MoveTo(x, texty);
  442.     if (inside_area(unit->x, unit->y)) {
  443.         sprintf(spbuf, "%d,%d", unit->x, unit->y);
  444.         if (unit->z != 0) {
  445.             tprintf(spbuf, ",%d", unit->z);
  446.         }
  447.     } else {
  448.         strcpy(spbuf, "  -  ");
  449.     }
  450.     DrawText(spbuf, 0, strlen(spbuf));
  451.     /* Draw the state of all the supplies. */
  452.     x += poscolw;
  453.     if (side_sees_unit(dside, unit)) {
  454.         for_all_material_types(m) {
  455.             MoveTo(x, texty);
  456.             sprintf(spbuf, "%d", unit->supply[m]);
  457.             DrawText(spbuf, 0, strlen(spbuf));
  458.             x += supcolw;
  459.         }
  460.     } else {
  461.         x += nummtypes * supcolw;
  462.     }
  463.     spbuf[0] = '\0';
  464.     if (side_sees_unit(dside, unit)
  465.         && !inside_area(unit->x, unit->y)
  466.         && unit_appear_turn(unit) >= 0) {
  467.         tprintf(spbuf, "appear %s", absolute_date_string(unit_appear_turn(unit)));
  468.     }
  469.     if (strlen(spbuf) > 0) {
  470.         MoveTo(x, texty);
  471.         DrawText(spbuf, 0, strlen(spbuf));
  472.     }
  473.     /* Fix the clipping. */
  474.     cliprect = list->window->portRect;
  475.     cliprect.right -= sbarwid;
  476.     ClipRect(&cliprect);
  477.     /* Highlight this entry if it was selected. */
  478.     if (list->contents->units[n].flag) {
  479.         InvertRect(&entryrect);
  480.     }
  481. }
  482.  
  483. void
  484. grow_list(List *list, int w, int h)
  485. {
  486.     WindowPtr listwin = list->window;
  487.  
  488.     SizeWindow(listwin, w, h, 1);
  489.     adjust_list_decor(list);
  490.     InvalRect(&listwin->portRect);
  491. }
  492.  
  493. /* Zooming works like a list view in the Finder - it calculates a "perfect" size,
  494.    showing as much as possible but with no wasted blank areas. */
  495.  
  496. void
  497. zoom_list(List *list, int part)
  498. {
  499.     int maxh;
  500.     WindowPtr listwin = list->window;
  501.  
  502.     if (part == inZoomOut) {
  503.         maxh = listtoph + list->numunits * list->entryspacing + sbarwid;
  504.         set_standard_state(listwin, maxlistwidth, maxh);
  505.     }
  506.     EraseRect(&listwin->portRect);
  507.     ZoomWindow(listwin, part, true);
  508.     adjust_list_decor(list);
  509. }
  510.  
  511. /* Move and size the scrollbars to reflect the list's window. */
  512.  
  513. void
  514. adjust_list_decor(List *list)
  515. {
  516.     int w, h;
  517.  
  518.     w = list->window->portRect.right - list->window->portRect.left;
  519.     h = list->window->portRect.bottom - list->window->portRect.top;
  520.     MoveControl(list->vscrollbar, w - sbarwid, listtoph - 1);
  521.     SizeControl(list->vscrollbar, sbarwid + 1, h - listtoph - sbarwid + 1);
  522.     MoveControl(list->hscrollbar, fixedfieldwidth - 1, h - sbarwid);
  523.     SizeControl(list->hscrollbar, w - fixedfieldwidth - sbarwid + 1, sbarwid + 1);
  524.     set_list_scrollbars(list);
  525. }
  526.  
  527. /* Temporary used by scroll procs (saves looking up from the control). */
  528.  
  529. List *curlist;
  530.  
  531. static pascal void
  532. list_vscroll_fn(ControlHandle control, short code)
  533. {
  534.     int curvalue, minvalue, maxvalue, oldvalue, pagesize, jump;
  535.     RgnHandle tmprgn;
  536.     Rect tmprect;
  537.     WindowPtr listwin = curlist->window;
  538.  
  539.     curvalue = oldvalue = GetCtlValue(control);
  540.     minvalue = GetCtlMin(control);
  541.     maxvalue = GetCtlMax(control);
  542.     pagesize = curlist->lastvisible - curlist->firstvisible + 1;
  543.     switch (code) {
  544.         case inPageDown:
  545.             jump = max(1, pagesize - 1);
  546.             break;
  547.         case inDownButton:
  548.             jump = 1;
  549.             break;
  550.         case inPageUp:
  551.             jump = min(-1, - (pagesize - 1));
  552.             break;
  553.         case inUpButton:
  554.             jump = -1;
  555.             break;
  556.         default:
  557.             jump = 0;
  558.             break;
  559.     }
  560.     curvalue = max(min(curvalue + jump, maxvalue), minvalue);
  561.     curlist->firstvisible = curvalue;
  562.     curlist->lastvisible = min(curlist->numunits, curlist->firstvisible + pagesize) - 1;
  563.     SetCtlValue(control, curvalue);
  564.     /* Scroll the already-drawn bits. */
  565.     tmprgn = NewRgn();
  566.     SetRect(&tmprect, 0, listtoph, listwin->portRect.right - sbarwid, listwin->portRect.bottom - sbarwid);
  567.     ScrollRect(&tmprect, 0, (oldvalue - curvalue) * curlist->entryspacing, tmprgn);
  568.     InvalRgn(tmprgn);
  569.     /* We'll need to redraw the headings line. */
  570.     draw_list_headings(curlist);
  571.     /* Do the update now, because we won't get back to the main event loop
  572.        until the mouse button is released. */
  573.     update_window(curlist->window);
  574.     DisposeRgn(tmprgn);
  575. }
  576.  
  577. static pascal void
  578. list_hscroll_fn(ControlHandle control, short code)
  579. {
  580.     int curvalue, minvalue, maxvalue, oldvalue, pagesize, jump;
  581.     RgnHandle tmprgn;
  582.     Rect tmprect;
  583.     WindowPtr listwin = curlist->window;
  584.  
  585.     oldvalue = GetCtlValue(control);
  586.     minvalue = GetCtlMin(control);
  587.     maxvalue = GetCtlMax(control);
  588.     pagesize = curlist->lastvisfield - curlist->firstvisfield;
  589.     switch (code) {
  590.         case inPageDown:
  591.             jump = pagesize - 10;
  592.             break;
  593.         case inDownButton:
  594.             jump = 10;
  595.             break;
  596.         case inPageUp:
  597.             jump = - (pagesize - 10);
  598.             break;
  599.         case inUpButton:
  600.             jump = -10;
  601.             break;
  602.         default:
  603.             jump = 0;
  604.             break;
  605.     }
  606.     curvalue = max(min(oldvalue + jump, maxvalue), minvalue);
  607.     curlist->firstvisfield = curvalue;
  608.     curlist->lastvisfield = curlist->firstvisfield + pagesize;
  609.     SetCtlValue(control, curvalue);
  610.     /* Scroll the already-drawn bits. */
  611.     tmprgn = NewRgn();
  612.     SetRect(&tmprect, 0, listtoph, listwin->portRect.right - sbarwid, listwin->portRect.bottom - sbarwid);
  613.     ScrollRect(&tmprect, 0, (oldvalue - curvalue) * curlist->entryspacing, tmprgn);
  614.     InvalRgn(tmprgn);
  615.     /* We'll need to redraw the headings line. */
  616.     draw_list_headings(curlist);
  617.     /* Do the update now, because we won't get back to the main event loop
  618.        until the mouse button is released. */
  619.     update_window(curlist->window);
  620.     DisposeRgn(tmprgn);
  621. }
  622.  
  623. /* Handle a mouse down in the list.  Grafport already set, mouse coords are local. */
  624.  
  625. /* (mouse downs should select/deselect list elements) */
  626.  
  627. void
  628. do_mouse_down_list(List *list, Point mouse, int mods)
  629. {
  630.     ControlHandle control;
  631.     short part;
  632.     int n, tmp;
  633.     WindowPtr window = list->window;
  634.  
  635.     if (list_vscroll_proc == NULL)
  636.       list_vscroll_proc = NewControlActionProc(list_vscroll_fn);
  637.     if (list_hscroll_proc == NULL)
  638.       list_hscroll_proc = NewControlActionProc(list_hscroll_fn);
  639.  
  640.     part = FindControl(mouse, window, &control);
  641.     if (control == list->vscrollbar) {
  642.         switch (part) {
  643.             case inThumb:
  644.                 part = TrackControl(control, mouse, NULL);
  645.                 if (part == inThumb) {
  646.                     list->firstvisible = GetCtlValue(control);
  647.                     force_update(window);
  648.                 }
  649.                 break;
  650.             default:
  651.                 curlist = list;
  652.                 part = TrackControl(control, mouse, list_vscroll_proc);
  653.                 list->firstvisible = GetCtlValue(control);
  654.                 break;
  655.         }
  656.     } else if (control == list->hscrollbar) {
  657.         switch (part) {
  658.             case inThumb:
  659.                 part = TrackControl(control, mouse, NULL);
  660.                 if (part == inThumb) {
  661.                     list->firstvisfield = GetCtlValue(control);
  662.                     force_update(window);
  663.                 }
  664.                 break;
  665.             default:
  666.                 curlist = list;
  667.                 part = TrackControl(control, mouse, list_hscroll_proc);
  668.                 list->firstvisfield = GetCtlValue(control);
  669.                 break;
  670.         }
  671.     } else {
  672.         if (mouse.v < listtoph) {
  673.             if (between(curactcolw, mouse.h, curactcolw + imagecolw)) {
  674.                 toggle_list_large_icons(list);
  675.             }
  676.             /* do others eventually */
  677.         } else {
  678.             /* Figure out the selected unit. */
  679.             n = (mouse.v - listtoph) / list->entryspacing + list->firstvisible;
  680.             tmp = list->contents->units[n].flag;
  681.             clear_selections(list);
  682.             list->contents->units[n].flag = !tmp;
  683.             redraw_unit_list_entry(list, n);
  684.         }
  685.     }
  686. }
  687.  
  688. void
  689. set_list_sorting(List *list, enum sortkeys newkey, int mi)
  690. {
  691.     int i;
  692.     
  693.     if (newkey != list->sortkeys[0]) {
  694.         /* Push all the existing sortkeys back - this way they'll can be
  695.            used as tiebreakers for the new sort key. */
  696.         for (i = MAXSORTKEYS - 1; i > 0; --i) {
  697.             list->sortkeys[i] = list->sortkeys[i - 1];
  698.         }
  699.         /* Add the new one onto the front. */
  700.         list->sortkeys[0] = newkey;
  701.         sort_list_contents(list);
  702.         force_update(list->window);
  703.         /* Record the menu item so it can get a checkmark during menu adjust. */
  704.         list->mainsortmi = mi;
  705.     }
  706. }
  707.  
  708. void
  709. toggle_list_large_icons(List *list)
  710. {
  711.     list->largeicons = !list->largeicons;
  712.     list->entryspacing = (list->largeicons ? largeentryspacing : smallentryspacing);
  713.     force_update(list->window);
  714. }
  715.  
  716. void
  717. update_unit_in_lists(Unit *unit)
  718. {
  719.     int line;
  720.     List *list;
  721.  
  722.     for_all_lists(list) {
  723.         if ((line = unit_position_in_list(list, unit)) >= 0) {
  724.             if (between(list->firstvisible, line, list->lastvisible)) {
  725.                 redraw_unit_list_entry(list, line);
  726.             }
  727.         } else {
  728.             /* (should attempt to insert at the correct location) */
  729.             add_unit_to_list(list, unit);
  730.             force_update(list->window);
  731.         }
  732.     }
  733. }
  734.  
  735. int
  736. unit_position_in_list(List *list, Unit *unit)
  737. {
  738.     int i;
  739.     
  740.     for (i = 0; i < list->numunits; ++i) {
  741.         if (unit == list->contents->units[i].unit) return i;
  742.     }
  743.     return (-1);
  744. }
  745.  
  746. void
  747. reorganize_list(List *list)
  748. {
  749.     organize_list_contents(list);
  750.     force_update(list->window);
  751. }
  752.  
  753. void
  754. redraw_unit_list_entry(List *list, int n)
  755. {
  756.     WindowPtr listwin;
  757.     Rect cliprect;
  758.     GrafPtr oldport;
  759.     RgnHandle tmprgn;
  760.  
  761.     if (!active_display(dside) || list == NULL) return;
  762.  
  763.     listwin = list->window;
  764.      GetPort(&oldport);
  765.     SetPort(listwin);
  766.     tmprgn = NewRgn();
  767.     GetClip(tmprgn);
  768.     /* Set up clipping for the contents of the list. */
  769.     cliprect = listwin->portRect;
  770.     cliprect.right -= sbarwid;
  771.     ClipRect(&cliprect);
  772.     TextSize(9);
  773.     draw_unit_list_entry(list, n, TRUE);
  774.     SetClip(tmprgn);
  775.     DisposeRgn(tmprgn);
  776.     SetPort(oldport);
  777. }
  778.  
  779. void
  780. clear_selections(List *list)
  781. {
  782.     int i;
  783.     
  784.     for (i = 0; i < list->numunits; ++i) {
  785.         if (list->contents->units[i].flag) {
  786.             list->contents->units[i].flag = FALSE;
  787.             redraw_unit_list_entry(list, i);
  788.         }
  789.     }
  790. }
  791.  
  792. Unit *
  793. selected_unit_in_list(List *list)
  794. {
  795.     int i;
  796.     
  797.     for (i = 0; i < list->numunits; ++i) {
  798.         if (list->contents->units[i].flag) return list->contents->units[i].unit;
  799.     }
  800.     return NULL;
  801. }
  802.  
  803. /* This finds a good map to scroll over to look at a unit mentioned in the list. */
  804.  
  805. void
  806. scroll_to_selected_unit_in_list(List *list)
  807. {
  808.     Unit *unit;
  809.  
  810.     /* Beep and return if there are no maps open currently. */
  811.     if (maplist == NULL) {
  812.         beep();
  813.         return;
  814.     }
  815.     unit = selected_unit_in_list(list);
  816.     if (unit != NULL && inside_area(unit->x, unit->y))
  817.       scroll_best_map_to_unit(unit);
  818. }
  819.  
  820. void
  821. activate_list(List *list, int activate)
  822. {
  823.     Rect growRect;
  824.  
  825.     if (activate) {
  826.         HiliteControl(list->vscrollbar, 0);
  827.         HiliteControl(list->hscrollbar, 0);
  828. #if 0
  829.         /* the controls must be redrawn on activation: */
  830.         (*(list->vscrollbar))->contrlVis = 255;
  831.         (*(list->hscrollbar))->contrlVis = 255;
  832.         InvalRect(&(*(list->vscrollbar))->contrlRect);
  833.         InvalRect(&(*(list->hscrollbar))->contrlRect);
  834. #endif
  835.         /* The growbox needs to be redrawn on activation. */
  836.         growRect = list->window->portRect;
  837.         /* adjust for the scrollbars */
  838.         growRect.top = growRect.bottom - sbarwid;
  839.         growRect.left = growRect.right - sbarwid;
  840.         InvalRect(&growRect);
  841.     } else {
  842.         /* The scrollbars must be hidden on deactivation. */
  843.         HiliteControl(list->vscrollbar, 255);
  844.         HiliteControl(list->hscrollbar, 255);
  845. /*        HideControl(list->vscrollbar);
  846.         HideControl(list->hscrollbar); */
  847.         /* The growbox should be changed immediately on deactivation. */
  848.         DrawGrowIcon(list->window);
  849.     }
  850. }
  851.  
  852. void
  853. print_list(List *list)
  854. {
  855. #if 0
  856.     TPPrPort printport;
  857.     extern THPrint printrecordhandle;
  858.  
  859.     printport = PrOpenDoc(printrecordhandle, nil, nil);
  860.     PrCloseDoc(printport);
  861. #endif
  862. }
  863.  
  864. /* Remove and destroy the list object. */
  865.  
  866. void
  867. destroy_list(List *list)
  868. {
  869.     List *list2;
  870.     
  871.     if (listlist == list) {
  872.         listlist = list->next;
  873.     } else {
  874.         for_all_lists(list2) {
  875.             if (list2->next == list) {
  876.                 list2->next = list->next;
  877.             }
  878.         }
  879.     }
  880.     /* (should destroy substructs) */
  881.     free(list);
  882. }
  883.